package com.yuanda.erp9.syn.service.erp9.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yuanda.erp9.syn.config.RestClientConfig;
import com.yuanda.erp9.syn.entity.CmsGoodsEntity;
import com.yuanda.erp9.syn.exception.TargetServerException;
import com.yuanda.erp9.syn.pojo.CmsGoodsPojo;
import com.yuanda.erp9.syn.service.erp9.ESService;
import com.yuanda.erp9.syn.util.DateUtils;
import com.yuanda.erp9.syn.util.SubListUtil;
import com.yuanda.erp9.syn.vo.EsCmsGoodsVo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName ESServiceImpl
 * @Description
 * @Date 2022/12/20
 * @Author myq
 */
@Service
@Slf4j
public class ESServiceImpl implements ESService {
    @Autowired
    private RestClientConfig restClientConfig;

    private final int count = 5000;

    private final String esDataType = "_doc";

    /**
     * 重试次数
     */
    private AtomicInteger maxRetryCount = new AtomicInteger(0);

    // 间隔时间
    private final long intervalTime = 60000;

    /**
     * @Description: 根据es主键删除数据
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/12/2011:04
     */
    @Override
    public void deleteAllByIds(List<String> ids) {
        if (!CollectionUtils.isEmpty(ids)) {
            RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
            try {
                List<List<String>> outherList = SubListUtil.splitList(ids, count);
                for (List<String> innerList : outherList) {
                    //批量删除数据
                    BulkRequest request = new BulkRequest();
                    request.timeout("60s");
                    for (String id : innerList) {
                        DeleteRequest source = new DeleteRequest().index(restClientConfig.getIndex()).id(id).type(esDataType);
                        request.add(source);
                    }
                    BulkResponse response = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
                    log.debug("ES批量删除数据 是否有失败 ： " + response.hasFailures());
                }
            } catch (IOException e) {
                e.printStackTrace();
                log.error("ES批量删除数据:  " + e.toString());
                throw new RuntimeException("ES批量删除数据:  {}", e);
            } finally {
                ids.clear();
                try {
                    restHighLevelClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @Description: 根据查看条件删除
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2023/2/1415:25
     */
    @Override
    public List<String> deleteByQueryBuilders(Integer supplierId, String hisp, Integer source) {
        //1. 创建SearchRequest
        SearchRequest request = new SearchRequest(restClientConfig.getIndex());
        request.types(esDataType);
        //2. 指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.should(QueryBuilders.matchQuery("supplier_id", supplierId));
        if (!StringUtils.isEmpty(hisp)) {
            boolQuery.should(QueryBuilders.matchQuery("hisp", hisp));
        }
        boolQuery.should(QueryBuilders.matchQuery("source", source));
        builder.query(boolQuery);

        request.source(builder);

        //3. 执行查询
        SearchResponse resp = null;
        try {
            resp = restClientConfig.restHighLevelClient().search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        List<String> ids = new ArrayList<>();
        Set<String> s = new HashSet<>();
        //打印命中数量
        log.info("命中总数量：{}", resp.getHits().getTotalHits());
        while (true) {
            for (SearchHit hit : resp.getHits()) {
                JSONObject jsonObject = JSON.parseObject(hit.getSourceAsString());
                ids.add(jsonObject.getString("goodid"));
                s.add(jsonObject.getString("supplier_id"));
            }
            if (resp.getHits().getHits().length == 0) {
                break;
            }
        }
        return ids;
    }

    /**
     * @Description: 根据查看条件删除
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2023/2/1415:25
     */
    @Override
    public void deleteBySupplierIdAndHispAndSource(Integer supplierId, String hisp, Integer source) {
        //通过QueryBuilders中的搜索逻辑
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
        //1 设置条件
        //设置删除条件: key = value
        TermQueryBuilder supplierIdTermQueryBuilder = QueryBuilders.termQuery("supplier_id", supplierId);
        if (!StringUtils.isEmpty(hisp)) {
            TermQueryBuilder hispTermQueryBuilder = QueryBuilders.termQuery("hisp", hisp);
            queryBuilder.must(hispTermQueryBuilder);
        }
        TermQueryBuilder sourceTermQueryBuilder = QueryBuilders.termQuery("source", source);
        queryBuilder.must(sourceTermQueryBuilder);
        queryBuilder.must(supplierIdTermQueryBuilder);

        //2 通过DeleteByQueryRequest来构建删除请求，setQuery来装载条件，indices来指定索引
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest();
        deleteByQueryRequest.setTimeout("6000s");
        deleteByQueryRequest.setQuery(queryBuilder);
        //指定删除索引
        deleteByQueryRequest.indices(restClientConfig.getIndex());
        deleteByQueryRequest.setConflicts("proceed");
        try {
            //3 通过deleteByQuery来发起删除请求
            BulkByScrollResponse deleteResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
            log.info("deleteData,删除成功，删除文档条数: " + deleteResponse.getDeleted() + " ,indexName：" + restClientConfig.getIndex());
        } catch (Exception e) {
            log.warn("调用ElasticSearch出现异常： count={} ,warn={}", maxRetryCount, e);
            // 开始重试
            if (!whileTry(supplierId, hisp, source)) {
                throw new TargetServerException("经过" + maxRetryCount + "次重试ElasticSearch客户端连接时仍然异常：{}" + e, supplierId, "");
            }
        } finally {
            try {
                if (restHighLevelClient != null) {
                    restHighLevelClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据条件删除
     *
     * @param supplierId
     * @param hisp
     * @param source
     * @param repertoryArea
     */
    @Override
    public void deleteBySupplierIdAndHispAndSourceAndRepertoryArea(Integer supplierId, String hisp, Integer source, String repertoryArea) {
        //通过QueryBuilders中的搜索逻辑
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
        //1 设置条件
        //设置删除条件: key = value
        TermQueryBuilder supplierIdTermQueryBuilder = QueryBuilders.termQuery("supplier_id", supplierId);
        if (!StringUtils.isEmpty(hisp)) {
            TermQueryBuilder hispTermQueryBuilder = QueryBuilders.termQuery("hisp", hisp);
            queryBuilder.must(hispTermQueryBuilder);
        }
        TermQueryBuilder sourceTermQueryBuilder = QueryBuilders.termQuery("source", source);
        TermQueryBuilder repertoryAreaTermQueryBuilder = QueryBuilders.termQuery("repertory_area", repertoryArea);
        queryBuilder.must(sourceTermQueryBuilder);
        queryBuilder.must(supplierIdTermQueryBuilder);
        queryBuilder.must(repertoryAreaTermQueryBuilder);

        //2 通过DeleteByQueryRequest来构建删除请求，setQuery来装载条件，indices来指定索引
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest();
        deleteByQueryRequest.setTimeout("6000s");
        deleteByQueryRequest.setQuery(queryBuilder);
        //指定删除索引
        deleteByQueryRequest.indices(restClientConfig.getIndex());
        deleteByQueryRequest.setConflicts("proceed");
        try {
            //3 通过deleteByQuery来发起删除请求
            BulkByScrollResponse deleteResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
            log.info("deleteData,删除成功，删除文档条数: " + deleteResponse.getDeleted() + " ,indexName：" + restClientConfig.getIndex());
        } catch (Exception e) {
            log.warn("调用ElasticSearch出现异常： count={} ,warn={}", maxRetryCount, e);
            // 开始重试
            if (!whileTry(supplierId, hisp, source)) {
                throw new TargetServerException("经过" + maxRetryCount + "次重试ElasticSearch客户端连接时仍然异常：{}" + e, supplierId, "");
            }
        } finally {
            try {
                if (restHighLevelClient != null) {
                    restHighLevelClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据条件查询商品详情
     *
     * @param supplier
     * @param model
     * @param source
     */
    @Override
    public List<EsCmsGoodsVo> findGoodsBySupplierIdAndModelAndSource(Integer supplier, String model, Integer source) {
        //通过QueryBuilders中的搜索逻辑
        RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
        List<EsCmsGoodsVo> list = new ArrayList<>();
        try {
            //1.创建 SearchRequest搜索请求,并指定要查询的索引
            SearchRequest searchRequest = new SearchRequest(restClientConfig.getIndex());

            //2.创建 SearchSourceBuilder条件构造。
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.sort("unit_price", SortOrder.ASC);

            BoolQueryBuilder mustQuery = QueryBuilders.boolQuery();
            mustQuery.must(QueryBuilders.termQuery("supplier_id", supplier));
            mustQuery.must(QueryBuilders.termQuery("model", model));
            mustQuery.must(QueryBuilders.termQuery("source", source));

            searchSourceBuilder.query(mustQuery);
            //3.将 SearchSourceBuilder 添加到 SearchRequest中
            searchRequest.source(searchSourceBuilder);

            //4.执行查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHits hits = searchResponse.getHits();
            if (hits.getTotalHits() == 0) {
                return null;
            }
            for (SearchHit hit : hits) {
                list.add(JSON.parseObject(hit.getSourceAsString(), EsCmsGoodsVo.class));
            }
            return list;
        } catch (IOException e) {

            e.printStackTrace();
            return null;
        } finally {
            try {
                restHighLevelClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Description: 间隔重试机制
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2023/2/2414:00
     */
    public boolean whileTry(Integer supplierId, String hisp, Integer source) {
        boolean retry = false;
        long range = intervalTime;
        long startTime = System.currentTimeMillis();
        while (!retry) {
            if ((System.currentTimeMillis() - startTime) > (range * (maxRetryCount.get()) + 1)) {
                if (maxRetryCount.incrementAndGet() <= 3) {
                    try {
                        log.info("%%%%%%%%%%%%第" + maxRetryCount.get() + "次重试%%%%%%%%%%%%%%%");
                        deleteBySupplierIdAndHispAndSource(supplierId, hisp, source);
                        retry = true;
                        maxRetryCount.set(1);
                    } catch (Exception e) {
                        retry = false;
                        log.error("无法连接到ES目标服务器count:" + maxRetryCount + "次，错误信息：{}", e);
                        e.printStackTrace();
                    }

                } else {
                    maxRetryCount.set(1);
                }
            }
        }
        return retry;
    }

    /**
     * @Description: 根据查看条件更新
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2023/2/1415:25
     */
    public void updateBySupplierIdAndHispAndSource(Integer supplierId, String hisp, Integer source, String value) {
        //通过QueryBuilders中的搜索逻辑
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //1 设置条件
        //设置删除条件: key = value
        TermQueryBuilder supplierIdTermQueryBuilder = QueryBuilders.termQuery("supplier_id", supplierId);
        if (!StringUtils.isEmpty(hisp)) {
            TermQueryBuilder hispTermQueryBuilder = QueryBuilders.termQuery("hisp", hisp);
            queryBuilder.must(hispTermQueryBuilder);
        }
        TermQueryBuilder sourceTermQueryBuilder = QueryBuilders.termQuery("source", source);
        queryBuilder.must(sourceTermQueryBuilder);
        queryBuilder.must(supplierIdTermQueryBuilder);

        //2 通过DeleteByQueryRequest来构建删除请求，setQuery来装载条件，indices来指定索引
        UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest();
        updateByQueryRequest.setTimeout("6000s");
        updateByQueryRequest.setQuery(queryBuilder);

        // 这个实际上就是对应给脚本中传参数的对象。
        HashMap<String, Object> params = new HashMap<>(16);
        params.put("hobby", "修改的参数value");
        final Script script = new Script(
                ScriptType.INLINE, "painless",
                "ctx._source.hobby = params.hobby",
                params);
        updateByQueryRequest.setScript(script);
        //指定索引
        updateByQueryRequest.indices(restClientConfig.getIndex());
        updateByQueryRequest.setConflicts("proceed");
        try {
            //3 通过deleteByQuery来发起删除请求
            BulkByScrollResponse deleteResponse = restClientConfig.restHighLevelClient().updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
            log.info("更新成功，更新文档条数: " + deleteResponse.getUpdated() + " ,indexName：" + restClientConfig.getIndex());
        } catch (IOException e) {
            e.printStackTrace();
            log.error("无法连接到ES目标服务器");
            throw new TargetServerException("无法连接到ES目标服务器");
        }
    }

    /**
     * @Description: 添加es数据
     * @Params:
     * @Return:
     * @Author: Mr.myq
     * @Date: 2022/12/2011:09
     */
    @Override
    public void addESData(List<CmsGoodsPojo> taskList) {
        List<CmsGoodsEntity> esDateList = new ArrayList<>();
        for (int i = 0; i < taskList.size(); i++) {
            esDateList.add(taskList.get(i).getCmsGoodsEntity());
            if (i != 0 && i % count == 0) {
                this.batchEsData(esDateList);
                esDateList.clear();
            }
        }
        if (!CollectionUtils.isEmpty(esDateList)) {
            this.batchEsData(esDateList);
        }
    }

    @Override
    public void updateESData(List<CmsGoodsEntity> list) {
        List<CmsGoodsEntity> esDateList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            esDateList.add(list.get(i));
            if (i != 0 && i % count == 0) {
                this.batchUpdateEsData(esDateList);
                esDateList.clear();
            }
        }
        if (!CollectionUtils.isEmpty(esDateList)) {
            this.batchUpdateEsData(esDateList);
        }
    }

    /**
     * 批量更新es数据
     *
     * @param esDateList
     */
    private void batchUpdateEsData(List<CmsGoodsEntity> esDateList) {
        if (CollectionUtils.isEmpty(esDateList)) {
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("600s");
        RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
        try {
            for (int i = 0; i < esDateList.size(); i++) {
                CmsGoodsEntity object = esDateList.get(i);
                Map<String, Object> param = new LinkedHashMap<>();
                String goodid = object.getGoodid();
                param.put("tariffs_rate", object.getTariffsRate());
                param.put("inspection_charges_fee", object.getInspectionChargesFee());
                param.put("rates", object.getRates());
                UpdateRequest updateRequest = new UpdateRequest(restClientConfig.getIndex(), "_doc", goodid);
                updateRequest.doc(param);
                bulkRequest.add(updateRequest);
            }
            // 操作ES
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (!bulk.hasFailures()) {
                log.info("******************批量更新ES成功*******************");
            }
        } catch (IOException e) {
            log.error("批量更新ES数据异常,e", e);
            e.printStackTrace();
            throw new RuntimeException("ES批量更新数据异常:{}" + e);
        } finally {
            try {
                restHighLevelClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            esDateList.clear();
        }
    }

    /**
     * 批量保存es数据
     *
     * @param data
     * @throws IOException
     */
    private void batchEsData(List<CmsGoodsEntity> data) {
        int a = 0;
        if (CollectionUtils.isEmpty(data)) {
            return;
        }
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("60s");
        RestHighLevelClient restHighLevelClient = restClientConfig.restHighLevelClient();
        try {
            for (int i = 0; i < data.size(); i++) {
                CmsGoodsEntity object = data.get(i);
                Map<String, Object> param = new LinkedHashMap<>();
                String goodid = object.getGoodid();
                param.put("goodid", this.isNull(goodid));
                param.put("adminid", this.isNull(object.getAdminid()));
                param.put("tenantsid", this.isNull(object.getTenantsid()));
                param.put("incodes", this.isNull(object.getIncodes()));
                param.put("classifyid", this.isNull(object.getClassifyid()));
                param.put("img", this.isNull(object.getImg()));
                param.put("type", "现货");
                param.put("type_id", 0);
                param.put("status", 1);
                param.put("status_msg", "");
                if(29 == object.getSupplier()){
                    param.put("data_type", "自营");
                    param.put("data_type_id", 2);
                }else {
                    param.put("data_type", "供应商");
                    param.put("data_type_id", 1);
                }
                param.put("model", this.isNull(object.getModel()));
                param.put("brand", this.isNull(object.getBrandName()));
                param.put("brand_id", this.isNull(object.getBrand()));
                param.put("supplier", this.isNull(object.getSplName()));
                param.put("supplier_id", this.isNull(object.getSupplier()));
                param.put("is_sample_apply", this.isNull(object.getIsSampleApply()));
                param.put("batch", this.isNull(object.getBatch()));
                param.put("original_price", 0);
                param.put("purchase_price", 0);
                param.put("unit_price", new BigDecimal(this.isNull(object.getUnitPrice())).setScale(4, BigDecimal.ROUND_UP));
                param.put("repertory", this.isNull(object.getRepertory()));
                param.put("repertory_area", this.isNull(object.getRepertoryArea()));
                param.put("publish", this.isNull(object.getPublish()));
                param.put("describe", this.isNull(object.getDescribe()));
                param.put("files", this.isNull(object.getFiles()));
                param.put("pay_type", 0);
                param.put("minimum_order", this.isNull(object.getMinimumOrder()));
                param.put("incremental", this.isNull(object.getIncremental()));
                param.put("mpq", this.isNull(object.getMpq()));
                param.put("delivery_area", this.isNull(object.getDeliveryArea()));
                param.put("domestic_date", this.isNull(object.getDomesticDate()));
                param.put("hongkong_date", this.isNull(object.getHongkongDate()));
                param.put("hisp", this.isNull(object.getHisp()));
                param.put("ccc", this.isNull(object.getCcc()));
                param.put("sales", 0);
                param.put("click", 0);
                param.put("visitor", 0);
                param.put("deleted_at", DateUtils.dateToZone(DateUtils.stringToDate(object.getDeletedAt(), DateUtils.simpleFormat)));
                param.put("created_at", DateUtils.dateToZone(DateUtils.stringToDate(object.getCreatedAt(), DateUtils.simpleFormat)));
                param.put("updated_at", DateUtils.dateToZone(DateUtils.stringToDate(object.getUpdatedAt(), DateUtils.simpleFormat)));
                param.put("activity_id", 0);
                param.put("atlas", this.isNull(object.getAtlas()));
                param.put("content", this.isNull(object.getContent()));
                param.put("eccn_no", this.isNull(object.getEccnNo()));
                param.put("tariffs_rate", this.isNull(object.getTariffsRate()));
                param.put("inspection_charges_fee", this.isNull(object.getInspectionChargesFee()));
                param.put("params", this.isNull(object.getParams()));
                param.put("prices", this.isNull(object.getPrices()));
                param.put("source", this.isNull(object.getSource()));
                param.put("rates", this.isNull(object.getRates().toString()));
                param.put("package", this.isNull(object.getPackage_()));
                param.put("packing", this.isNull(object.getPacking()));

                IndexRequest indexRequest = new IndexRequest(restClientConfig.getIndex());
                //文档id唯一
                indexRequest.id(goodid).source(JSONObject.toJSONString(param), XContentType.JSON).type(esDataType);
                bulkRequest.add(indexRequest);
                a++;
            }
            // 操作ES
            BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (!bulk.hasFailures()) {
                log.info("******************批量新增ES成功*******************" + a);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("ES批量添加数据异常:{}" + e);
        } finally {
            try {
                restHighLevelClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            data.clear();
        }
    }

    private String isNull(String param) {
        if (StringUtils.isEmpty(param)) {
            return "";
        }
        return param;
    }

    private Integer isNull(Integer param) {
        if (param == null) {
            return 0;
        }
        return param;
    }

    private BigDecimal isNull(BigDecimal param) {
        if (param == null) {
            return BigDecimal.ZERO;
        }
        return param;
    }

    private Double isNull(Double param) {
        if (param == null) {
            return Double.valueOf("0");
        }
        return param;
    }

    private Object isNull(Object param) {
        if (param == null) {
            return "";
        }
        return param;
    }
}